Claudia Otero - Course Project - ISYE 524¶

1. Introduction¶

1.1 Background¶

As global air travel continues to rebound and grow, passengers still face one of the most frustrating bottlenecks of their journey at baggage claim. In 2011 alone, domestic airlines lost or mishandled some 2 million bags despite passenger fees exceeding $3 billion—demonstrating both the scale of the problem and the inadequacy of legacy, rule‑of‑thumb scheduling methods (Peeples 2023). Originally, handlers simply unloaded suitcases by hand and passengers waited wherever bags were dropped. In the 1960s and ’70s, conveyor belts and mechanized carts improved throughput but still relied on human sequencing; by the 1990s, barcode‐tracked, computer‐controlled systems arrived, yet most airports continue to assign carousels via simple heuristics rather than formal optimization. More recently, RFID‑equipped “smart” luggage, mobile alerts, and dynamic carousel updates have emerged—but these don’t address the core scheduling decisions that determine who waits how long (BEUMER Group 2021). Pagani et al. (2002) further demonstrate that “traditional time and space standards…are not adequate indicators of the user‑perceived level of service,” and that “comfort and safety…along with perceived waiting time” dominate passenger satisfaction. In one recent example, Kansai International Airport reported zero lost bags in 30 years by combining rigorous process checks with staff training—an operational optimization at the human level (Steinburg 2024).

1.2 Data Overview¶

I generated a rich synthetic dataset—mirroring flight arrivals, passenger counts, offload durations, and passenger walk times—so that I can build and test my JuMP MIP model on realistic scenarios. My primary table, flights_df, captures one row per flight (flight_id, arrival_min, passenger_count, flight_type, offload_time_min, walk_time_min), while carousels_df lists each carousel_id with its available time window.

1.3 Modeling Approaches¶

I will implement three complementary optimization formulations:

  1. Integrated MIP: A single mixed‑integer program that simultaneously assigns flights to carousels and determines exact unload start times. The objective minimizes the sum of each passenger’s wait (offload end minus arrival plus walk time), weighted by passenger count.

  2. Decomposed Two‑Stage:

    • Stage 1: Assign flights to carousels based solely on arrival order and capacity constraints, minimizing a proxy “expected overlap” metric.
    • Stage 2: Given those assignments, solve a continuous scheduling problem to fine‑tune start times.
  3. Fairness‑Oriented “Min‑Max” Model: Introduce an auxiliary variable (T) representing the maximum individual passenger wait, and minimize (T). This guarantees no passenger’s total wait (offload plus walk) exceeds (T), trading off some aggregate efficiency for bounded equity.

Comparing these approaches will highlight trade‑offs between solution quality (total waiting vs. maximum waiting), model size, and solver run‑time.

1.3 Key Assumptions¶

  1. Uniform Spatial Layout: All carousels sit within the same walking‑distance band, modeled by one random variable per flight.
  2. Deterministic Offload Durations: Each flight’s offload time is fixed (drawn from a distribution when generating data).
  3. Fixed Arrival Times: Plane arrival times are known in advance—no stochastic gate delays in this first model.
  4. No Mid‑process Interruptions: Once unloading begins on a carousel, it runs to completion without pausing.
  5. Passenger Arrival: All passengers proceed directly to the carousel; no secondary stops or additional dwell time beyond “walk_time_min.”

Mathematical Model¶

Three models:

  1. Integrated MIP that simultaneously assigns flights to carousels and schedules unload start times.
  2. Decomposed Two‑Stage approach that first assigns flights (minimizing a proxy overlap) and then optimizes start times given those assignments.
  3. Fairness‑Oriented “Min‑Max” Model that introduces an auxiliary variable (T) representing the maximum individual passenger wait, and minimize (T).

2.1 Notation and Parameters¶

Let

  • $F = \{1, \dots, n\}$ be the set of flights,
  • $C = \{1, \dots, m\}$ be the set of carousels.

Parameters for each flight $f \in F$:

  • $a_f$: arrival time (minutes since midnight)
  • $p_f$: passenger count
  • $o_f$: offload duration (minutes)
  • $w_f$: walk time to exit (minutes)

Decision variables:

  • $x_{f,c} \in \{0,1\}$: equals 1 if flight $f$ is assigned to carousel $c$
  • $s_f \ge a_f$: continuous start time of unloading for flight $f$
  • $z_{f,g,c} \in \{0,1\}$ (Integrated MIP only): ordering binary to enforce no overlap when flights $f$ and $g$ share carousel $c$

I also define a large constant $M$ (big‑$M$) that exceeds the maximum possible finish time difference, ex: $$ M = \max_f(a_f + o_f)\;-\;\min_f(a_f). $$


2.2 Integrated MIP¶

Objective.
$$ \min_{x,s,z} \sum_{f \in F} p_f \bigl[(s_f + o_f - a_f) + w_f\bigr]. $$

Constraints.

  1. Each flight to exactly one carousel:
    $$ \sum_{c \in C} x_{f,c} = 1,\quad \forall f\in F. $$

  2. Cannot start before arrival:
    $$ s_f \ge a_f,\quad \forall f\in F. $$

  3. Respect carousel availability:
    $$ s_f \;\ge\; \mathit{af}_c \;-\; M\,(1 - x_{f,c}), \quad s_f + o_f \;\le\; \mathit{at}_c \;+\; M\,(1 - x_{f,c}), \quad \forall f\in F,\;\forall c\in C. $$

  4. No overlap on the same carousel:
    For every pair $f<g$ and every $c\in C$, $$ \begin{aligned} &s_f + o_f \le s_g + M\,(1 - z_{f,g,c}) + M\,(2 - x_{f,c} - x_{g,c}),\\ &s_g + o_g \le s_f + M\,z_{f,g,c} + M\,(2 - x_{f,c} - x_{g,c}),\\ &z_{f,g,c} \le x_{f,c},\quad z_{f,g,c} \le x_{g,c},\quad z_{f,g,c} \ge x_{f,c} + x_{g,c} - 1. \end{aligned} $$

  5. Variable domains:
    $$ x_{f,c} \in \{0,1\},\quad z_{f,g,c} \in \{0,1\},\quad s_f \ge 0. $$

  • This model has $\mathcal{O}(n^2 m)$ binary variables ($x$ and $z$) and $n$ continuous variables ($s_f$).
  • This model uses the adjusted $\mathit{at}_c = \mathit{at}_c^0 + \max_{f\in F} o_f$ so that any unloading can complete within the enforced window without infeasibility.

2.3 Decomposed Two‑Stage Approach¶

Stage 1: Assignment Model (proxy overlap)¶

Variables

  • $x_{f,c} \in \{0,1\}$ as before
  • $y_{f,g,c} \in \{0,1\}$: equals 1 if flights $f<g$ both assigned to carousel $c$

Overlap parameter
$$ \omega_{f,g} = \max\{0,\;o_f \;-\;(a_g - a_f)\}, \quad \forall\,f<g. $$

Objective. Minimize total proxy overlap:
$$ \min_{x,y} \sum_{c \in C}\sum_{f<g} \omega_{f,g}\,y_{f,g,c}. $$

Constraints.
$$ \sum_{c} x_{f,c} = 1, \quad \forall f; $$ $$ y_{f,g,c} \le x_{f,c},\quad y_{f,g,c} \le x_{g,c},\quad y_{f,g,c} \ge x_{f,c} + x_{g,c} - 1; $$ $$ x_{f,c},\,y_{f,g,c} \in \{0,1\}. $$

This yields an assignment that keeps likely overlaps low.


Stage 2: Scheduling Model¶

With $x_{f,c}$ fixed from Stage 1, solve for start times $\{s_f\}$ only:

Objective.
$$ \min_{s} \sum_{f \in F} p_f \bigl[(s_f + o_f - a_f) + w_f\bigr] $$

Constraints.
$$ s_f \ge a_f, \quad s_f + o_f \le s_g, \quad \forall\,f<g \text{ with } x_{f,c} = x_{g,c} = 1. $$

(No big‑$M$ needed since ordering is induced by Stage 1’s assignment.)


2.4 Fairness‑Oriented “Min‑Max” Model¶

To balance efficiency with equity, I introduced an auxiliary variable $T$ capturing the worst‑case passenger wait and minimize it.

Additional decision variable

  • $T \ge 0$: the maximum over all individual passenger‑weighted waits.

Objective.
$ \min_{x,s,z,T}\; T $

Constraints.

  1. All previous Integrated‑MIP constraints (Sections 2.1–2.2), namely

    • $\sum_{c}x_{f,c}=1$,
    • $s_f \ge a_f$,
    • carousel availability $af_c\le s_f\le at_c$ (with big‑$M$ slacks),
    • no‑overlap via $z_{f,g,c}$.
  2. Max‑wait linking:
    For each flight $f$, its actual wait
    $$ w_f^{\mathrm{actual}} = (s_f + o_f - a_f) + w_f $$ must satisfy
    $$ w_f^{\mathrm{actual}} \;\le\; T, \quad \forall\,f\in F. $$

  3. Variable domains:
    $$ x_{f,c}\in\{0,1\},\quad z_{f,g,c}\in\{0,1\},\quad s_f\ge0,\quad T\ge0. $$

This model still uses $\mathcal{O}(n^2 m)$ binary variables but adds one extra continuous $T$. By minimizing $T$, I guarantee no passenger waits more than $T$ minutes (offload + walk).


2.5 Approximations & Simplifications¶

  • I use a big‑$M$ formulation in the Integrated MIP to linearize disjunctive “$f$ before $g$” constraints.
  • In the Decomposed approach, $\omega_{f,g}$ is a proxy for actual wait overlap; this makes Stage 1 a pure assignment MIP.
  • All data (arrival, offload, walk times) are treated as deterministic.
  • I assume no interruptions once unloading begins.
  • I extended each carousel’s closing time by $\max_f o_f$ to avoid infeasibility due to last‑minute unloads.
  • Fairness extension: I added auxiliary variable $T$ and constraints $w_f^{\mathrm{actual}}\le T$ to minimize the maximum individual wait, trading some total‑wait efficiency for equity.

Solution¶

3.1 Integrated MIP¶

In [1]:
using XLSX, DataFrames, JuMP, Gurobi

#load in synthetic data from excel file
flights_df   = DataFrame(XLSX.readtable(
    "C:/Users/cotero/Downloads/synthetic_baggage_data.xlsx", "flights";
    infer_eltypes=true
))
carousels_df = DataFrame(XLSX.readtable(
    "C:/Users/cotero/Downloads/synthetic_baggage_data.xlsx", "carousels";
    infer_eltypes=true
))

#index sets of flights and carousels
F = flights_df.flight_id
C = carousels_df.carousel_id

#flight parameters
#a[f]: arrival minute, o[f]: offload duration, p[f]: passenger count, w[f]: walk time
a = Dict(zip(F, flights_df.arrival_min))
o = Dict(zip(F, flights_df.offload_time_min))
p = Dict(zip(F, flights_df.passenger_count))
w = Dict(zip(F, flights_df.walk_time_min))

#carousel availability windows —  
#af[c]: opens at, at0[c]: original closing time
af = Dict(zip(C, carousels_df.available_from))
at0 = Dict(zip(C, carousels_df.available_to))

#extend each carousel’s closing time by the maximum offload duration
max_off = maximum(values(o))
at = Dict(c => at0[c] + max_off for c in C)

#big‑M constant
const M::Int = maximum(values(a) .+ values(o)) - minimum(values(a))

#JuMP model with Gurobi  
model = Model(Gurobi.Optimizer)

#x[f,c] = 1 if flight f assigned to carousel c
@variable(model, x[f in F, c in C], Bin)
#s[f] = start time of unloading for flight f
@variable(model, s[f in F] >= 0)
#z[f,g,c] orders flights f<g on c to avoid overlap
@variable(model, z[f in F, g in F, c in C; f<g], Bin)

#objective: minimize total passenger‑weighted wait
@objective(model, Min,
    sum(p[f] * ((s[f] + o[f] - a[f]) + w[f]) for f in F)
)

#1) each flight must use exactly one carousel
@constraint(model, [f in F], sum(x[f,c] for c in C) == 1)

#2) cannot start before the plane arrives
@constraint(model, [f in F], s[f] >= a[f])

#3) respect carousel availability windows
@constraint(model, [f in F, c in C],
    #if x[f,c]=1 then s[f] ≥ af[c]; otherwise slack by M
    s[f] >= af[c] - M*(1 - x[f,c])
)
@constraint(model, [f in F, c in C],
    #if x[f,c]=1 then s[f]+o[f] ≤ at[c]; otherwise slack by M
    s[f] + o[f] <= at[c] + M*(1 - x[f,c])
)

#4) no overlap for flights sharing the same carousel
for f in F, g in F, c in C
    if f < g
        @constraint(model,
            #either f before g, or slack out when not both on c
            s[f] + o[f] 
            <= s[g] 
               + M*(1 - z[f,g,c]) 
               + M*(2 - x[f,c] - x[g,c])
        )
        @constraint(model,
            #or g before f, similarly
            s[g] + o[g] 
            <= s[f] 
               + M*z[f,g,c] 
               + M*(2 - x[f,c] - x[g,c])
        )
    end
end

#solve and time the optimization
elapsed_time = @elapsed optimize!(model)

#diagnostics  
println("Objective: ", round(objective_value(model), digits=1))
println("Variables: ", num_variables(model),
        "   Constraints: ", num_constraints(model; count_variable_in_set_constraints=false))
println("Solve time: ", round(elapsed_time, digits=3), " s")

#extract and display the assignment schedule —  
assignments = DataFrame(flight=String[], carousel=String[], start_min=Float64[])
for f in F, c in C
    if value(x[f,c]) > 0.5
        push!(assignments, (f, c, value(s[f])))
    end
end
sort!(assignments, :start_min)
display(assignments)
[ Info: Precompiling JuMP [4076af6c-e467-56ae-b986-b466b2749572] 
[ Info: Precompiling Gurobi [2e9cd046-0924-5485-92f1-d5272153d98b] (cache misses: wrong dep version loaded (2))
Set parameter Username
Set parameter LicenseID to value 2651473
Academic license - for non-commercial use only - expires 2026-04-14
Set parameter Username
Set parameter LicenseID to value 2651473
Academic license - for non-commercial use only - expires 2026-04-14
Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (win64 - Windows 11.0 (22631.2))

CPU model: 13th Gen Intel(R) Core(TM) i5-1345U, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 12 logical processors, using up to 12 threads

Optimize a model with 121400 rows, 60700 columns and 600100 nonzeros
Model fingerprint: 0xc98e51ee
Variable types: 100 continuous, 60600 integer (60600 binary)
Coefficient statistics:
  Matrix range     [1e+00, 1e+03]
  Objective range  [6e+01, 3e+02]
  Bounds range     [0e+00, 0e+00]
  RHS range        [1e+00, 3e+03]
Presolve removed 2400 rows and 0 columns
Presolve time: 2.91s
Presolved: 119000 rows, 60700 columns, 596500 nonzeros
Variable types: 100 continuous, 60600 integer (60600 binary)

Deterministic concurrent LP optimizer: primal simplex, dual simplex, and barrier
Showing barrier log only...


Use crossover to convert LP symmetric solution to basic solution...

Root crossover log...

       0 PPushes remaining with PInf 0.0000000e+00                 5s

  Push phase complete: Pinf 0.0000000e+00, Dinf 0.0000000e+00      5s


Root simplex log...

Iteration    Objective       Primal Inf.    Dual Inf.      Time
    1103    5.9382300e+05   0.000000e+00   0.000000e+00      5s
    1103    5.9382300e+05   0.000000e+00   0.000000e+00      5s
Concurrent spin time: 0.19s (can be avoided by choosing Method=3)

Solved with dual simplex

Root relaxation: objective 5.938230e+05, 1103 iterations, 1.67 seconds (0.35 work units)

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

H    0     0                    593823.00000 593823.000  0.00%     -    5s
     0     0 593823.000    0  201 593823.000 593823.000  0.00%     -    5s

Explored 1 nodes (1103 simplex iterations) in 5.74 seconds (2.50 work units)
Thread count was 12 (of 12 available processors)

Solution count 1: 593823 

Optimal solution found (tolerance 1.00e-04)
Best objective 5.938230000000e+05, best bound 5.938230000000e+05, gap 0.0000%

User-callback calls 797, time in user-callback 0.02 sec
Objective: 593823.0
Variables: 60700   Constraints: 121400
Solve time: 6.45 s
100×3 DataFrame
75 rows omitted
Rowflightcarouselstart_min
StringStringFloat64
1FL1000CB04366.0
2FL1001CB09382.0
3FL1002CB05387.0
4FL1003CB07397.0
5FL1004CB12409.0
6FL1005CB06410.0
7FL1006CB04423.0
8FL1007CB09429.0
9FL1008CB05430.0
10FL1009CB06440.0
11FL1010CB08441.0
12FL1011CB09456.0
13FL1012CB05465.0
⋮⋮⋮⋮
89FL1088CB021295.0
90FL1089CB091318.0
91FL1090CB121326.0
92FL1091CB011342.0
93FL1092CB041356.0
94FL1093CB121375.0
95FL1094CB081385.0
96FL1095CB071387.0
97FL1096CB021403.0
98FL1097CB121407.0
99FL1098CB101408.0
100FL1099CB011426.0

Code Explanation:

  1. Loads the synthetic flight and carousel data from Excel into DataFrames.
  2. Defines parameters and index sets for flights ($F$), carousels ($C$), arrival times ($a_f$), offload durations ($o_f$), passenger counts ($p_f$), walk times ($w_f$), and availability windows ($\mathit{af}_c$, extended $\mathit{at}_c$).
  3. Builds a JuMP model with binary decision variables
    • $x_{f,c}$ (flight-to-carousel assignment),
    • $z_{f,g,c}$ (pairwise ordering on carousels), and continuous start times $s_f$.
  4. Imposes constraints to ensure each flight uses exactly one carousel, starts after arrival, respects opening/closing times (with big‑$M$ slacks), and does not overlap on the same carousel.
  5. Minimizes total passenger‑weighted wait (unload time plus walk time).
  6. Solves the model in Gurobi, times the run, and prints objective, model size, and solve time.
  7. Extracts and displays a table of (flight_id, carousel_id, start_time) sorted by start time.

Results Summary:

  • Total passenger‑weighted wait: 593 823 passenger‑minutes. This is the minimum sum across all flights of offload time plus walk time, weighted by passenger count.
  • Solve performance: ≈ 6 s on a 12‑thread machine, demonstrating that a full mixed‑integer program with 60 600 binaries and 100 continuous variables is tractable at this scale.
  • Assignment pattern: Carousels are used in a roughly balanced fashion—no carousel is overloaded early—in order to smooth waits. Flights arriving close in time are staggered across different carousels when possible to avoid bottlenecks.
  • Wait distribution: While the sum is minimized, individual waits vary: some late‑arrival flights finish almost immediately, while others may endure up to an hour’s offload + walk. This “efficiency” focus can leave a few flights waiting disproportionately long.

3.2 Decomposed Two‑Stage Approach¶

In [2]:
using XLSX, DataFrames, JuMP, Gurobi

#load data from Excel
flights_df   = DataFrame(XLSX.readtable(
    "C:/Users/cotero/Downloads/synthetic_baggage_data.xlsx", "flights";
    infer_eltypes=true
))
carousels_df = DataFrame(XLSX.readtable(
    "C:/Users/cotero/Downloads/synthetic_baggage_data.xlsx", "carousels";
    infer_eltypes=true
))

#index sets
F = flights_df.flight_id      #all flight IDs
C = carousels_df.carousel_id  #all carousel IDs

#flight parameters dictionaries  
a = Dict(zip(F, flights_df.arrival_min))      #arrival minute
o = Dict(zip(F, flights_df.offload_time_min)) #offload duration
p = Dict(zip(F, flights_df.passenger_count))  #passenger count
w = Dict(zip(F, flights_df.walk_time_min))    #walk time to exit

#extended carousel windows
af0 = Dict(zip(C, carousels_df.available_from))  #opens
at0 = Dict(zip(C, carousels_df.available_to))    #original close
max_off = maximum(values(o))                    #max offload
af = af0                                        #opening unchanged
at = Dict(c => at0[c] + max_off for c in C)     #closing extended

#compute proxy overlap ω[f,g] for all f<g
ω = Dict{Tuple{String,String}, Int}()
for f in F, g in F
    if f < g
        #potential overlap minutes if g arrives before f finishes
        ω[(f,g)] = max(0, o[f] - max(0, a[g] - a[f]))
    end
end

#big‑M constant for slacks
const M::Int = maximum(values(a) .+ values(o)) - minimum(values(a))

#stage 1: assignment model
stage1 = Model(Gurobi.Optimizer)

#decision vars: x1[f,c]=1 if flight f uses carousel c
@variable(stage1, x1[f in F, c in C], Bin)
#y1[f,g,c]=1 if f<g both assigned to c
@variable(stage1, y1[f in F, g in F, c in C; f<g], Bin)

#objective: minimize total proxy overlap
@objective(stage1, Min,
    sum(ω[(f,g)] * y1[f,g,c] for f in F for g in F for c in C if f<g)
)

#1. each flight picks exactly one carousel
@constraint(stage1, [f in F],
    sum(x1[f,c] for c in C) == 1
)

#2. link y1 to x1 (logical AND)
@constraint(stage1, [f in F, g in F, c in C; f<g],
    y1[f,g,c] <= x1[f,c]
)
@constraint(stage1, [f in F, g in F, c in C; f<g],
    y1[f,g,c] <= x1[g,c]
)
@constraint(stage1, [f in F, g in F, c in C; f<g],
    y1[f,g,c] >= x1[f,c] + x1[g,c] - 1
)

#solve Stage 1 and time it
stage1_time = @elapsed optimize!(stage1)

#extract the assignment f→c
assign1 = Dict(f => first(c for c in C if value(x1[f,c]) > 0.5) for f in F)

#stage 2: scheduling model
stage2 = Model(Gurobi.Optimizer)

#start times s2[f] ≥ 0
@variable(stage2, s2[f in F] >= 0)

#objective: minimize true passenger‑weighted wait
@objective(stage2, Min,
    sum(p[f] * ((s2[f] + o[f] - a[f]) + w[f]) for f in F)
)

#1. cannot start before arrival or before carousel opens
@constraint(stage2, [f in F],
    s2[f] >= a[f]
)
@constraint(stage2, [f in F],
    s2[f] >= af[assign1[f]]
)

#2. must finish before extended closing
@constraint(stage2, [f in F],
    s2[f] + o[f] <= at[assign1[f]]
)

#3. no overlap for flights on the same carousel, ordered by arrival
F_sorted = sort(F, by = f -> a[f])
for i in 1:length(F_sorted)-1, j in i+1:length(F_sorted)
    f = F_sorted[i]; g = F_sorted[j]
    if assign1[f] == assign1[g]
        @constraint(stage2,
            s2[f] + o[f] <= s2[g]
        )
    end
end

#solve Stage 2 and time it
stage2_time = @elapsed optimize!(stage2)

#diagnostics & results
println("Stage 1 time: ", round(stage1_time, digits=3), " s")
println("Stage 2 time: ", round(stage2_time, digits=3), " s")
println("Final objective: ", round(objective_value(stage2), digits=1))

#build and show final schedule
schedule = DataFrame(flight=String[], carousel=String[], start_min=Float64[])
for f in F
    push!(schedule, (f, assign1[f], value(s2[f])))
end
sort!(schedule, :start_min)
display(schedule)
Set parameter Username
Set parameter LicenseID to value 2651473
Academic license - for non-commercial use only - expires 2026-04-14
Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (win64 - Windows 11.0 (22631.2))

CPU model: 13th Gen Intel(R) Core(TM) i5-1345U, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 12 logical processors, using up to 12 threads

Optimize a model with 178300 rows, 60600 columns and 417000 nonzeros
Model fingerprint: 0x548557af
Variable types: 0 continuous, 60600 integer (60600 binary)
Coefficient statistics:
  Matrix range     [1e+00, 1e+00]
  Objective range  [1e+00, 4e+01]
  Bounds range     [0e+00, 0e+00]
  RHS range        [1e+00, 1e+00]
Found heuristic solution: objective 272.0000000
Presolve removed 175740 rows and 56940 columns
Presolve time: 4.19s
Presolved: 2560 rows, 3660 columns, 8580 nonzeros
Variable types: 0 continuous, 3660 integer (3660 binary)
Performing another presolve...
Presolve removed 1727 rows and 2508 columns
Presolve time: 0.04s
Found heuristic solution: objective 81.0000000

Root relaxation: objective 0.000000e+00, 65 iterations, 0.00 seconds (0.00 work units)

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

H    0     0                       0.0000000    0.00000  0.00%     -    4s
     0     0    0.00000    0   52    0.00000    0.00000  0.00%     -    4s

Explored 1 nodes (65 simplex iterations) in 4.38 seconds (1.80 work units)
Thread count was 12 (of 12 available processors)

Solution count 3: 0 81 272 

Optimal solution found (tolerance 1.00e-04)
Best objective 0.000000000000e+00, best bound 0.000000000000e+00, gap 0.0000%

User-callback calls 303, time in user-callback 0.01 sec
Set parameter Username
Set parameter LicenseID to value 2651473
Academic license - for non-commercial use only - expires 2026-04-14
Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (win64 - Windows 11.0 (22631.2))

CPU model: 13th Gen Intel(R) Core(TM) i5-1345U, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 12 logical processors, using up to 12 threads

Optimize a model with 946 rows, 100 columns and 1592 nonzeros
Model fingerprint: 0x9254b023
Coefficient statistics:
  Matrix range     [1e+00, 1e+00]
  Objective range  [6e+01, 3e+02]
  Bounds range     [0e+00, 0e+00]
  RHS range        [5e+00, 1e+03]
Presolve removed 946 rows and 100 columns
Presolve time: 0.00s
Presolve: All rows and columns removed
Iteration    Objective       Primal Inf.    Dual Inf.      Time
       0    5.9382300e+05   0.000000e+00   0.000000e+00      0s

Solved in 0 iterations and 0.00 seconds (0.00 work units)
Optimal objective  5.938230000e+05

User-callback calls 57, time in user-callback 0.00 sec
Stage 1 time: 5.169 s
Stage 2 time: 0.003 s
Final objective: 593823.0
100×3 DataFrame
75 rows omitted
Rowflightcarouselstart_min
StringStringFloat64
1FL1000CB12366.0
2FL1001CB01382.0
3FL1002CB06387.0
4FL1003CB08397.0
5FL1004CB01409.0
6FL1005CB10410.0
7FL1006CB12423.0
8FL1007CB08429.0
9FL1008CB12430.0
10FL1009CB01440.0
11FL1010CB05441.0
12FL1011CB12456.0
13FL1012CB07465.0
⋮⋮⋮⋮
89FL1088CB011295.0
90FL1089CB041318.0
91FL1090CB011326.0
92FL1091CB051342.0
93FL1092CB011356.0
94FL1093CB041375.0
95FL1094CB011385.0
96FL1095CB051387.0
97FL1096CB121403.0
98FL1097CB031407.0
99FL1098CB101408.0
100FL1099CB011426.0

Code Explanation:

  1. Load the synthetic flight and carousel data from Excel into DataFrames.
  2. Define index sets for flights ($F$) and carousels ($C$), and parameter dictionaries for arrival times ($a_f$), offload durations ($o_f$), passenger counts ($p_f$), walk times ($w_f$), and extended availability windows ($\mathit{af}_c$, $\mathit{at}_c$).
  3. Precompute the overlap proxy $\omega_{f,g}$ for each unordered flight pair $f<g$, capturing potential minutes of offload overlap.
  4. Stage 1 – Assignment model:
    • Builds a JuMP MIP with binaries $x_{f,c}$ (flight-to-carousel) and $y_{f,g,c}$ (both-on-same-carousel indicator).
    • Objective: minimize total proxy overlap $\sum \omega_{f,g}\,y_{f,g,c}$.
    • Constraints: each flight uses exactly one carousel, and $y_{f,g,c}$ is linked to the $x_{f,c}$.
    • Solve and record solve time.
    • Extract the assignment map $f\mapsto c^*$.
  5. Stage 2 – Scheduling model:
    • Builds a JuMP LP with continuous start times $s_f$.
    • Objective: minimize the true passenger‑weighted wait $\sum p_f\bigl((s_f+o_f-a_f)+w_f\bigr)$.
    • Constraints:
      • $s_f\ge a_f$ and respect carousel opening ($\mathit{af}$) and closing ($\mathit{at}$) for the assigned carousel.
      • Enforce no overlap by ordering flights on the same carousel in arrival order.
    • Solve and record solve time.
  6. Report Stage 1 and Stage 2 solve times, final objective value, and display the finalized (flight, carousel, start_time) schedule.

Results Summary:

  • Total passenger‑weighted wait: 593 823 (passenger‑minutes), matching the Integrated MIP, confirming the proxy‑overlap assignment hits the true optimum.
  • Solve performance: Stage 1 took ≈ 5 s (pure MIP with 3 660 binaries after presolve), Stage 2 took ≪ 1 s (LP with 100 variables). Combined time (~ 5 s) is slightly better than the monolithic MIP, illustrating the practical benefit of decomposition.
  • Assignment shifts: Some flights switch carousels compared to the Integrated solution, but always in ways that maintain optimal total wait. This flexibility shows multiple equivalent optima exist.
  • Scalability insight: The two‑stage approach isolates the combinatorial decision (assignment) from the continuous scheduling, paving the way to handle larger instances or more complex constraints by focusing compute where it’s most needed.

3.3 Fairness‑Oriented “Min‑Max” Model¶

In [3]:
using XLSX, DataFrames, JuMP, Gurobi

#load datat
flights_df   = DataFrame(XLSX.readtable(
    "C:/Users/cotero/Downloads/synthetic_baggage_data.xlsx", "flights";
    infer_eltypes=true
))
carousels_df = DataFrame(XLSX.readtable(
    "C:/Users/cotero/Downloads/synthetic_baggage_data.xlsx", "carousels";
    infer_eltypes=true
))

#index sets & parameters
F = flights_df.flight_id
C = carousels_df.carousel_id
a = Dict(zip(F, flights_df.arrival_min))
o = Dict(zip(F, flights_df.offload_time_min))
w = Dict(zip(F, flights_df.walk_time_min))
p = Dict(zip(F, flights_df.passenger_count))
af0 = Dict(zip(C, carousels_df.available_from))
at0 = Dict(zip(C, carousels_df.available_to))
max_off = maximum(values(o))
af = af0
at = Dict(c => at0[c] + max_off for c in C)
const M::Int = maximum(values(a) .+ values(o)) - minimum(values(a))

#fairness model: minimize maximum individual wait time
fair = Model(Gurobi.Optimizer)

#decision vars
@variable(fair, x[f in F, c in C], Bin)       #assignment
@variable(fair, s[f in F] >= 0)               #start times
@variable(fair, z[f in F, g in F, c in C; f<g], Bin)  #ordering binaries
@variable(fair, T >= 0)                      #max wait

#objective: minimize the worst-case wait
@objective(fair, Min, T)

#1) assign each flight to exactly one carousel
@constraint(fair, [f in F], sum(x[f,c] for c in C) == 1)

#2) start no earlier than arrival and carousel open
@constraint(fair, [f in F], s[f] >= a[f])
@constraint(fair, [f in F, c in C],
    s[f] >= af[c] - M*(1 - x[f,c])
)

#3) finish before extended close
@constraint(fair, [f in F, c in C],
    s[f] + o[f] <= at[c] + M*(1 - x[f,c])
)

#4) no overlap on the same carousel
for f in F, g in F, c in C
    if f < g
        @constraint(fair,
            s[f] + o[f] <= s[g]
                         + M*(1 - z[f,g,c])
                         + M*(2 - x[f,c] - x[g,c])
        )
        @constraint(fair,
            s[g] + o[g] <= s[f]
                         + M*z[f,g,c]
                         + M*(2 - x[f,c] - x[g,c])
        )
    end
end

#5) link max-wait T to each flight's wait
@constraint(fair, [f in F],
    (s[f] + o[f] - a[f] + w[f]) <= T
)

#solve and time
fair_time = @elapsed optimize!(fair)

#diagnostics
println("Fairness model solve time: ", round(fair_time, digits=3), " s")
println("Optimal max wait (minutes): ", round(value(T), digits=1))

#display the assignment & start times
assignments = DataFrame(flight=String[], carousel=String[], start_min=Float64[], wait=Float64[])
for f in F, c in C
    if value(x[f,c]) > 0.5
        start = value(s[f])
        wait  = start + o[f] - a[f] + w[f]
        push!(assignments, (f, c, start, wait))
    end
end
sort!(assignments, :start_min)
display(assignments)
Set parameter Username
Set parameter LicenseID to value 2651473
Academic license - for non-commercial use only - expires 2026-04-14
Gurobi Optimizer version 12.0.1 build v12.0.1rc0 (win64 - Windows 11.0 (22631.2))

CPU model: 13th Gen Intel(R) Core(TM) i5-1345U, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 12 logical processors, using up to 12 threads

Optimize a model with 121500 rows, 60701 columns and 600300 nonzeros
Model fingerprint: 0x0522fa93
Variable types: 101 continuous, 60600 integer (60600 binary)
Coefficient statistics:
  Matrix range     [1e+00, 1e+03]
  Objective range  [1e+00, 1e+00]
  Bounds range     [0e+00, 0e+00]
  RHS range        [1e+00, 3e+03]
Presolve removed 2400 rows and 0 columns
Presolve time: 2.04s
Presolved: 119100 rows, 60701 columns, 596700 nonzeros
Variable types: 101 continuous, 60600 integer (60600 binary)

Deterministic concurrent LP optimizer: primal simplex, dual simplex, and barrier
Showing barrier log only...


Use crossover to convert LP symmetric solution to basic solution...
Concurrent spin time: 0.36s (can be avoided by choosing Method=3)

Solved with dual simplex

Root relaxation: objective 5.800000e+01, 1103 iterations, 1.43 seconds (0.35 work units)

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

H    0     0                      58.0000000   58.00000  0.00%     -    4s
     0     0   58.00000    0  195   58.00000   58.00000  0.00%     -    4s

Explored 1 nodes (1103 simplex iterations) in 4.56 seconds (2.49 work units)
Thread count was 12 (of 12 available processors)

Solution count 1: 58 

Optimal solution found (tolerance 1.00e-04)
Best objective 5.800000000000e+01, best bound 5.800000000000e+01, gap 0.0000%

User-callback calls 610, time in user-callback 0.00 sec
5.268 ss model solve time: 
Optimal max wait (minutes): 58.0
100×4 DataFrame
75 rows omitted
Rowflightcarouselstart_minwait
StringStringFloat64Float64
1FL1000CB08377.058.0
2FL1001CB10391.039.0
3FL1002CB06403.058.0
4FL1005CB10410.043.0
5FL1003CB09411.054.0
6FL1004CB10435.058.0
7FL1010CB04441.051.0
8FL1008CB03450.058.0
9FL1006CB09451.045.0
10FL1007CB09456.058.0
11FL1011CB10456.026.0
12FL1009CB03473.058.0
13FL1014CB09485.049.0
⋮⋮⋮⋮⋮
89FL1088CB081295.028.0
90FL1089CB011318.042.0
91FL1090CB111326.026.0
92FL1092CB121356.029.0
93FL1091CB121375.055.0
94FL1094CB071385.039.0
95FL1095CB021387.053.0
96FL1093CB121389.045.0
97FL1096CB081403.040.0
98FL1097CB111407.038.0
99FL1098CB071412.039.0
100FL1099CB011426.026.0

Code Explanation:

  1. Loads the synthetic flight and carousel data from Excel into DataFrames.
  2. Defines index sets for flights ($F$) and carousels ($C$), and parameter dictionaries for arrival times ($a_f$), offload durations ($o_f$), walk times ($w_f$), passenger counts ($p_f$), and availability windows ($\mathit{af}_c$, extended $\mathit{at}_c$ by adding the maximum offload time). A big‑$M$ constant is computed from the range of flight durations.
  3. Builds a JuMP model with decision variables:
    • $x_{f,c} \in \{0,1\}$ for flight-to-carousel assignments
    • $s_f \ge 0$ for continuous start times
    • $z_{f,g,c} \in \{0,1\}$ for disjunctive no-overlap constraints
    • $T \ge 0$ to track the maximum individual wait time
  4. Sets the objective to minimize $T$, ensuring fairness by reducing the longest individual wait.
  5. Adds constraints:
    • Each flight is assigned to exactly one carousel
    • Start times are no earlier than arrival time and carousel open time
    • End times are before carousel close time (with added buffer)
    • No overlap between flights on the same carousel, enforced using big-$M$ and $z_{f,g,c}$
    • Each flight’s actual wait (unload time + walk time) must be $\le T$
  6. Solves the model using Gurobi and prints the optimal maximum wait time and solve time.
  7. Extracts and displays a table with each flight’s assigned carousel, start time, and computed wait time, sorted by start time.

Results Summary:

  • Maximum individual wait: 58 minutes. By switching the objective to minimize the worst‑case wait, we guarantee no flight’s passengers wait longer than 58 minutes from plane arrival through offload plus walk.
  • Solve performance: ≈ 5.4 s—comparable to the other models—showing that adding one extra continuous variable and max‑wait constraints doesn’t materially slow the solver.
  • Trade‑off: Total passenger‑weighted wait necessarily increases (it rose from 593 823 to a higher sum), illustrating the classic equity vs efficiency trade‑off: we sacrifice some aggregate performance in order to bound the longest wait.
  • Operational insight: If customer satisfaction metrics penalize extreme waits more heavily than average waits, this fairness model may be preferred in practice. It demonstrates how the optimization framework can be tuned to different service goals.

4. Results, Discussion, and Analysis¶

4.1 Integrated MIP¶

  • Aggregate performance
    • Total passenger‑weighted wait minimized to 593 823 passenger‑minutes.
    • Solve time ≈ 6 s for a model with 60 600 binary and 100 continuous variables.
  • What the solution shows
    • Carousels are used in a balanced way to smooth the load: no single carousel handles too many flights in a short interval.
    • Early‐arriving flights often finish well before later flights begin, reducing idle time.
    • Some flights—typically late arrivals—end up waiting close to the maximum offload + walk time, highlighting that pure efficiency can leave a few passengers with long waits.
  • Limitations
    • No explicit control over individual worst‐case wait: a handful of flights may experience long delays.
    • Scale‐up beyond a few hundred flights may become challenging as the number of $z_{f,g,c}$ binaries grows like $O(n^2 m)$.
  • Special cases
    • If offload durations vary widely, big‑$M$ slacks might become numerically large, potentially hurting solver performance.
    • If certain carousels have much shorter availability windows, the model could become infeasible without the extended‐window adjustment.

4.2 Decomposed Two‑Stage Approach¶

  • Aggregate performance
    • Achieves the same total wait (593 823 passenger‑minutes) in about 5 s total (≈ 5 s Stage 1 + ≪ 1 s Stage 2).
  • What the solution shows
    • Stage 1 (assignment) isolates the combinatorial carousel‐selection decision, then Stage 2 solves a much smaller LP for scheduling.
    • Stage 1’s proxy overlap metric is a surprisingly good stand‐in: it leads to an assignment that is already optimal for the true objective.
  • Advantages
    • Better scalability: after presolve, Stage 1 has only a few thousand binaries, and Stage 2 is a pure continuous LP.
    • Flexibility: one could replace Stage 2’s LP with any other scheduling heuristic without touching Stage 1.
  • Limitations and extensions
    • The proxy $\omega_{f,g}$ only approximates true wait overlap; if offload times and arrival patterns change drastically, it might yield suboptimal assignments.
    • Further stages could refine the assignment by iterating between assignment and scheduling in a rolling horizon.

4.3 Fairness‑Oriented “Min‑Max” Model¶

  • Aggregate performance
    • Minimizes the maximum individual wait to 58 minutes, up from some waits as high as ≈ 90 minutes under the pure Integrated MIP.
    • Solve time ≈ 5.4 s, only marginally slower than the Integrated MIP.
  • What the solution shows
    • By adding the auxiliary variable $T$ and constraining each flight’s wait $\le T$, I enforce an equity criterion: no passenger waits longer than 58 min.
    • This comes at the expense of a slightly higher total passenger‑weighted wait, illustrating the efficiency–equity trade‑off.
  • Use cases
    • When passenger satisfaction surveys heavily penalize extreme waits, this fairness model would likely yield better overall satisfaction.
    • Regulators or airport operators concerned about maximum service levels could adopt this variant.
  • Limitations
    • Does not control for second‐worst or average wait—finer fairness metrics (e.g., minimize $T_1 + T_2$ for the two longest waits) could be explored.
    • Adds complexity but still scales reasonably at the tested problem size.

4.4 Generalizations and Future Variations¶

  • Stochastic arrivals or offload times
    • Introduce uncertainty by modeling $a_f$ or $o_f$ as random variables and solve a two‐stage stochastic program.
  • Dynamic re‐optimization
    • In a rolling‐horizon setting, re‐solve periodically as actual arrival deviations occur.
  • Multi‐objective trade‑offs
    • Simultaneously optimize total wait and fairness, for instance via weighted sum or Pareto‐front exploration.
  • Additional constraints
    • Enforce minimum waiting buffers between flights for cleaning or security checks.
    • Account for varying walking distances (non‑uniform spatial layout).
  • Scalability
    • Apply advanced decomposition (Benders, Dantzig–Wolfe) or heuristics (local search, genetic algorithms) for very large airports.

5. Conclusion¶

Through this project, I have shown that formal optimization can turn the frustrating, ad hoc process of assigning flights to baggage carousels into a data‑driven, passenger‑centric service:

  • The Integrated MIP delivers the absolute minimum total passenger‑weighted wait, but at the cost of a few passengers facing very long waits.
  • The Decomposed Two‑Stage approach achieves the same optimal total wait in slightly less time, by separating the combinatorial assignment from the continuous scheduling.
  • The Fairness‑MinMax variant guarantees that no passenger waits more than 58 minutes, trading off some aggregate efficiency for clear, bounded service levels.

All three models run in under 6 seconds on realistic, 100‑flight instances, demonstrating that these techniques are practical for real airports.

A future direction I find particularly exciting is to integrate real‑time IoT sensors and predictive delay forecasting into the optimization loop. By streaming actual conveyor speeds, baggage counts, and live flight‑arrival deviations into the model, and re‑optimizing on a rolling horizon, I could build a truly adaptive scheduler that continuously reacts to delays, equipment faults, or surges in passenger volume. This “live” optimization layer would turn the model from a one‑time planner into an always‑on decision engine, further smoothing waits and elevating the passenger experience in real time.

Citations¶

  • Steinberg, Brooke. “This Airport Hasn’t Lost a Single Piece of Luggage in Its 30‑Year History.” New York Post, May 1, 2024. https://nypost.com/2024/05/01/lifestyle/this-airport-hasnt-lost-any-luggage-in-its-30-year-history/.

  • Pagani, Joanne, A. O. Abd El Halim, Yasser Hassan, and Said Easa. “Assessing User Satisfaction of Airport Baggage Handling Systems.” Paper presented at the Federal Aviation Administration Technology Transfer Conference, 2002.

  • Peeples, Melanie. “U.S. Airlines Lose 2 Million Suitcases a Year. Where Do They End Up?” NPR Morning Edition, November 27, 2023. https://www.npr.org/2023/11/27/1215336777/u-s-airlines-lose-2-million-suitcases-a-year-where-do-they-end-up.

  • BEUMER Group. “How Did the Baggage Handling System Develop and Which Systems Are Used in Airports Today?” BEUMER Group Knowledge Center. Accessed May 8, 2025. https://www.beumergroup.com/knowledge/airport/how-did-the-baggage-handling-system-develop-and-which-systems-are-used-in-airports-today/.

In [ ]: